feat(llm): llama.cpp come motore predefinito per estrazione ed embedding - #5
Conversation
Il server di inferenza self-hosted espone la forma OpenAI, quindi LlamaCppProvider usa /v1/chat/completions con response_format json_object: llama.cpp lo implementa vincolando il sampling con una grammatica JSON, quindi l'output e' ben formato per costruzione e non per buona volonta' del modello. Conta, visto che il chiamante fa un json.loads secco. Gli embedding sono selezionati separatamente (EMBEDDING_PROVIDER) da KG_LLM_PROVIDER: l'estrazione si cambia liberamente, gli embedding no, perche' dimensionano l'indice vettoriale. Il batch parte ora in un'unica richiesta invece di un round-trip per testo, e i vettori sono riordinati per "index" invece che per posizione: un batch permutato in silenzio attaccherebbe ogni vettore al chunk sbagliato. CAMBIO CHE RICHIEDE REINDICIZZAZIONE: Qwen3-Embedding-0.6B produce 1024 dimensioni contro le 768 di nomic-embed-text, quindi REDIS_VECTOR_DIM passa a 1024 e un'installazione con dati esistenti va ricostruita. create_index inghiottiva ogni eccezione: con l'indice gia' esistente a 768 la creazione era un no-op silenzioso e il disallineamento emergeva come query che non restituivano nulla. Ora la larghezza viene confrontata e l'avvio si interrompe stampando il FT.DROPINDEX da eseguire. Chi vuole restare su Ollama dichiara EMBEDDING_PROVIDER=ollama e REDIS_VECTOR_DIM=768. La validazione della forma della risposta sta fuori dal retry: una risposta troncata e' una violazione di contratto, non un guasto transitorio, e ritentarla ritardava soltanto l'errore di ~15 secondi. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 998f4130c8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| # Default engine: the self-hosted llama.cpp server on the host, reached via | ||
| # host.docker.internal — chat on :8080 (llama-swap), embeddings on :8081. | ||
| # Both are read from .env (LLAMACPP_BASE_URL / LLAMACPP_EMBEDDING_BASE_URL). |
There was a problem hiding this comment.
Update the root env file for the new inference defaults
When users follow the documented quick start (cp .env.example .env) and launch docker-compose.yml, these URLs are not actually provided because the root .env.example was left unchanged: it contains no LLAMACPP_* variables and still sets REDIS_VECTOR_DIM=768. The API therefore uses localhost:8080/8081 from inside its container while selecting llama.cpp, and creates a 768-dimensional index for the new 1024-dimensional embeddings; the GHCR compose also inherits the stale 768 value through variable substitution. Update the root template alongside the API-specific template so the default deployment can reach inference and build the correct index.
Useful? React with 👍 / 👎.
| KG_LLM_PROVIDER: "${KG_LLM_PROVIDER:-llamacpp}" | ||
| EMBEDDING_PROVIDER: "${EMBEDDING_PROVIDER:-llamacpp}" | ||
| REDIS_VECTOR_DIM: "${REDIS_VECTOR_DIM:-1024}" | ||
| LLAMACPP_BASE_URL: "${LLAMACPP_BASE_URL:-http://host.docker.internal:8080}" |
There was a problem hiding this comment.
Move llama.cpp off the MCP host port
When the full production stack is run with the documented host llama.cpp server, this default collides with the MCP service: both compose files publish MCP as host 8080:8080, while llama.cpp is also expected to listen on host port 8080. Consequently either llama.cpp prevents the MCP container from binding or MCP occupies the address intended for chat completions. Assign one service a distinct host port and update LLAMACPP_BASE_URL consistently.
Useful? React with 👍 / 👎.
| # "llamacpp" (default) → the self-hosted llama.cpp inference server. | ||
| # "ollama" → the legacy local-inference behaviour. | ||
| # "anthropic" → Claude (Haiku by default), opt-in. | ||
| KG_LLM_PROVIDER: str = "llamacpp" |
There was a problem hiding this comment.
Probe the selected inference providers in health checks
With this new default, GET /health still unconditionally requests OLLAMA_BASE_URL/api/tags in api/main.py and never checks either llama.cpp endpoint. A healthy default llama.cpp-only deployment is therefore reported as degraded, while an unrelated live Ollama instance can make health appear healthy even when extraction and embedding inference are unavailable. Select the health probes from KG_LLM_PROVIDER and EMBEDDING_PROVIDER.
Useful? React with 👍 / 👎.
Punta knowledge-graph al server di inferenza self-hosted llama.cpp, per l'estrazione e per gli embedding. Claude resta disponibile con
KG_LLM_PROVIDER=anthropic, Ollama con=ollama.Per l'estrazione,
LlamaCppProviderusa/v1/chat/completionsconresponse_format: json_object. Non è una richiesta cortese al modello: llama.cpp lo implementa vincolando il sampling con una grammatica JSON, quindi l'output è ben formato per costruzione. Conta, visto che il chiamante fa unjson.loadssecco.Qwen3-Embedding-0.6Bproduce 1024 dimensioni contro le 768 dinomic-embed-text. Vettori di modelli diversi non sono confrontabili e l'indice Redis è dimensionato sull'ampiezza del modello, quindi un'installazione con dati esistenti va ricostruita:Per restare su Ollama senza reindicizzare:
EMBEDDING_PROVIDER=ollama+REDIS_VECTOR_DIM=768.Difetto preesistente corretto qui
create_indexinghiottiva ogni eccezione. Con l'indice già esistente a 768 la creazione era quindi un no-op silenzioso, e il disallineamento non emergeva lì ma molto più tardi, come query che non restituivano nulla. Ora la larghezza dell'indice vivo viene confrontata conREDIS_VECTOR_DIMe l'avvio si interrompe stampando il comando da eseguire.Altre due cose
Embedding separati dall'estrazione (
EMBEDDING_PROVIDERdistinto daKG_LLM_PROVIDER): l'estrazione si cambia liberamente, gli embedding no, perché dimensionano l'indice. Tenerli sulla stessa manopola invitava a cambiarli per sbaglio.Il batch parte in una sola richiesta invece di un round-trip per testo (misurato: 64 testi / 1142 token in 0,58 s), e i vettori vengono riordinati per il campo
indexinvece che per posizione — un batch permutato in silenzio attaccherebbe ogni vettore al chunk sbagliato, corruzione che nulla a valle intercetterebbe. C'è un test che lo verifica con un mock che restituisce le righe deliberatamente invertite.La validazione della forma della risposta sta fuori dal retry: una risposta troncata è una violazione di contratto, non un guasto transitorio, e ritentarla ritardava solo l'errore di ~15 secondi.
Verifica
POST :8081/v1/embeddings→ dim 1024docker-composevalidati condocker compose config🤖 Generated with Claude Code